昨天我們學會怎麼做分頁了,今天我們要來學習怎麼在 API 實作搜尋與排序,方便使用者可以找到他想找到的資料。
首先我們來做排序功能,讓我們打開 server/app/todo/views.py
並依照下方說明修改
-from rest_framework import decorators, response, viewsets
+from rest_framework import decorators, filters, response, viewsets
from server.app.todo import models as todo_models
from server.app.todo import serializers as todo_serializers
class TaskViewSet(viewsets.ModelViewSet):
queryset = todo_models.Task.objects.order_by("id")
serializer_class = todo_serializers.TaskSerializer
+ filter_backends = (filters.OrderingFilter,)
+ ordering_fields = ("id", "title")
def get_serializer_class(self):
if self.action == "create":
return todo_serializers.TaskCreateSerializer
return super().get_serializer_class()
# ...... 以下省略 ......
上面的代表說我們在這個 ViewSet 設定一個 Filter 是 OrderingFilter 讓使用者可以排序,並設定可以排序的欄位為 id
以及 title
這時候大家是著使用 GET 請求下方的網址(要記得啟動虛擬環境 & 伺服器唷)
因為我們有設定可排序欄位為 id 以及 title 所以可以將上方 url 參數換成 title
同時他也支援多個欄位排序例如 http://127.0.0.1:8000/api/todo/tasks?ordering=title,-id 代表先依照 title 排序(由小到大)再依照 id 排序(由大到小)
現在我們一樣是在單一 ViewSet 中設定 Filter,現在讓我們把它移到全域去
打開 server/app/todo/views.py
並依照下方修改
-from rest_framework import decorators, filters, response, viewsets
+from rest_framework import decorators, response, viewsets
from server.app.todo import models as todo_models
from server.app.todo import serializers as todo_serializers
class TaskViewSet(viewsets.ModelViewSet):
queryset = todo_models.Task.objects.order_by("id")
serializer_class = todo_serializers.TaskSerializer
- filter_backends = (filters.OrderingFilter,)
ordering_fields = ("id", "title")
def get_serializer_class(self):
if self.action == "create":
return todo_serializers.TaskCreateSerializer
return super().get_serializer_class()
# ...... 以下省略 ......
接著打開 server/settings.py
並修改
# ...... 以上省略 ......
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": [
"rest_framework_simplejwt.authentication.JWTAuthentication",
],
"DEFAULT_PERMISSION_CLASSES": [
"rest_framework.permissions.IsAuthenticated",
],
+ "DEFAULT_FILTER_BACKENDS": [
+ "rest_framework.filters.OrderingFilter",
+ ],
"DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema",
"DEFAULT_PAGINATION_CLASS": "server.utils.pagination.PageNumberPagination",
"PAGE_SIZE": 10,
}
# ...... 以下省略 .......
現在可以再次請求看看剛剛的測試,應該會發現功能應該都一樣。接著我們來在其他 ViewSet 也加上排序的設定,讓我們打開 server/app/todo/views.py
並修改
# ...... 以上省略 ......
class TagViewSet(viewsets.ModelViewSet):
queryset = todo_models.Tag.objects.order_by("id")
serializer_class = todo_serializers.TagSerializer
+ ordering_fields = ("id", "name")
接著大家可以試看看 Tag 列表的 API 應該也具有排序功能囉~
接著我們來做搜尋功能讓我們打開 server/settings.py
P.S. 這邊我就直接在全域設定 Filter 單獨的設定方法與前面說的相同
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": [
"rest_framework_simplejwt.authentication.JWTAuthentication",
],
"DEFAULT_PERMISSION_CLASSES": [
"rest_framework.permissions.IsAuthenticated",
],
"DEFAULT_FILTER_BACKENDS": [
"rest_framework.filters.OrderingFilter",
+ "rest_framework.filters.SearchFilter",
],
"DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema",
"DEFAULT_PAGINATION_CLASS": "server.utils.pagination.PageNumberPagination",
"PAGE_SIZE": 10,
}
上方的改動是設定全部的 ViewSet 除了排序的 Filter 以外需要額外加上 Search 的 Filter
接著打開 server/app/todo/views.py
並修改
# ...... 以上省略 ......
class TaskViewSet(viewsets.ModelViewSet):
queryset = todo_models.Task.objects.order_by("id")
serializer_class = todo_serializers.TaskSerializer
ordering_fields = ("id", "title")
+ search_fields = ("title", "description")
# ...... 以下省略 ......
這邊的設定是當使用者搜尋關鍵字時要在這兩個欄位尋找(是模糊搜尋其中一個欄位出現關鍵字即符合)
現在是著用 GET 方法請求 http://127.0.0.1:8000/api/todo/tasks?search=關鍵字 就可以搜尋標題或說明有出現關鍵字的任務。
現在我們來讓 Tag 相關的 API 也支援搜尋,讓我們打開 server/app/todo/views.py
並修改
# ...... 以上省略 ......
class TagViewSet(viewsets.ModelViewSet):
queryset = todo_models.Tag.objects.order_by("id")
serializer_class = todo_serializers.TagSerializer
ordering_fields = ("id", "name")
+ search_fields = ("name",)
現在我們的 Tag API 也支援搜尋名稱了。
今天我們學會了如何做搜尋與排序。
結束前別忘了檢查一下今天的程式碼有沒有問題,並排版好喔。
ruff check --fix .
black .
pyright .
今天的內容就到這邊了,明天我們會學習如何做篩選,一起期待一下吧。
P.S. 今天的檔案更新可以參考我的 Git Commit 大家可以搭配服用